MySQL ロック
#MySQL #DB
概要
まずはロックの基本を押さえておくと大体把握できる。
加えて、MySQLにおけるロックでポイントとなってくる箇所を記述しておく。
ロックモード
普通のRDBMSと同じく、MySQLでも「共有ロック(S)」「排他ロック(X)」が基本。
https://scrapbox.io/files/63aafe5b73051c001db382d1.png
ポイント
InnoDB は、テーブルインデックスを検索またはスキャンするときに、生成されたインデックスレコード上に共有ロックまたは排他ロックを設定するという方法で、行レベルロックを実行します。
つまり、スキャンされた対象レコード(存在するしないに関わらず)に対してロックが取られてる!!
ロック範囲と実装
注)全てのロックにおいて、共有(読み取り)or排他(書き込み)の2モードが存在する
テーブルレベルロック
LOCK TABLES <table名> <read | write>でテーブルロックが可能。
読み取りロックの場合、他トランザクションからのS系、読み取りロックとは競合が可能になる。
UNLOCK TABLESでロック解除可能。
また、トランザクション内で1つのテーブルロックを持ってるときは、別テーブルにアクセスできなくなるので注意。
行レベルロック(InnoDBの話)
レコードロック
1つのレコードに対して取るロック。
もっと厳密に言うと、インデックスレコードに対してロックしてる。
InnoDBでは、主キーがあろうが無かろうが、必ずクラスタインデックスが作られてる。
このクラスタインデックスに対してロックしてる。
(前提:クラスタインデックスのリーフに、レコードそれぞれの全カラム値が入ってるよ)
ギャップロック
隣り合うインデックスレコードの間に対してのロック。
このギャップロックが取られてる際は、そこに新しいレコードを挿入できなくなる。
ex)id=3, id=8の2つのレコードが存在したとして、この間のギャップロックを取得してると...
id=4 ~ 8までの新しいレコードは挿入できなくなる
8!?ってなってると思うが、8に対してもロックが取られるので注意されたし。
前(id=3)は取られない。
重要.icon ちなみに、ロックモードに関係なく、ギャップロック同士は競合しない。
重要.icon ギャップロックの存在理由
ファントムリードを防ぐために存在する
MySQL :: MySQL 5.6 リファレンスマニュアル :: 14.2.7 ネクストキーロックによるファントム問題の回避
所感.icon
InnoDBには、MVCCというものがあるので、基本的にはあまり気にする場面もなさそうonigiri.w2.icon
MVCCの仕組みがあれば、最初に読み取ったレコードを崩さずにトランザクションの最後まで保持してくれる。
REPEATABLE READの場合。
挿入インテンションギャップロック
INSERT時に取得されるギャップロック。
ex)id=3, id=8というレコードが存在してる状況で、id=5のレコードを挿入する場合、4 =7に対しての挿入インテンションギャップロックがとられる。
挿入インテンションギャップロック同士は、主キーが同じレコードを挿入しない限りは競合しない。
TxA, TxBのどちらともが、id=5のレコードを挿入しようとすると、後から挿入しようとした方が待たされる。
ただし、TxAがid=4, TxBがid=5とかで同じでないなら待ちは発生しない。
ネクストキーロック
レコードロックとギャップロックを合わせたロック。
テーブルスキャン時に存在するレコードには「レコードロックをかける。
存在しないレコード部分に対しては「ギャップロック」をかける。
ex)id = 92, id = 103というレコードが存在したとして、id > 100の共有ロックをかけた場合...
id = 93からid=103に対してギャップロック、id=103に対してレコードロック、id>=104に対してギャップロックをかける。
インテンションロック
テーブルレベルと行レベルのロック関係の橋渡し的な役割。
行レベルロックをかける前に、いつもこのインテンションロックが取られてる。
例えば、行レベルロック(S)を取ろうと思ったら、インテンションロック(IS)が先に取られてる。
もし、テーブルレベルロックがかけられてたら、インテンションロック(IS)が取得できなくて、行レベルロックも取得できずに待ちが発生する。
という、そういう実装、仕組み。
その他注意点・ポイント
漢(オトコ)のコンピュータ道: InnoDBのREPEATABLE READにおけるLocking Readについての注意点
世界の何処かで MySQL(InnoDB)の REPEATABLE READ に嵌る人を1人でも減らすために - KAYAC engineers' blog
挿入と参照ロックに疲れ果てた俺たちは - ichirin2501's diary
参考
MySQL のロック概観重要.icon
MySQL(InnoDB)の行ロック - フリエン生活
MySQLの行ロックを図解 - Qiita重要.icon
手を動かしながらロックを学ぶ 2重要.icon
hr.icon
調査ログ
https://scrapbox.io/files/63ba5e84b8e1cb001ede2dd7.png
インデックスが貼られてないレコードでの更新では、テーブルロックがかかってしまう。
MySQLでのロックの範囲は基本的に以下
テーブルロック
LOCK TABLES <table> (read | write)でロックする。
テーブル全体にロックをかけるので、あらゆる行レベルロックが取得できなくなる。
ただし、READの場合は、読み取り系ロックなら取得可能。
テーブルロックを取得してるトランザクションは、他のテーブルにアクセスできなくなる。
行レベル
インテンションロック
テーブルロックと行ロックの間を取り持つためのロック
例えばテーブルロック(X)がどこぞのTxに取得されてる際、行ロックは取得できなくなるのだが、その取得できない動きを実現するために必要なロックがこれ。
レコードロック
こいつは即ち、クラスタインデックスをロックするってこと。
InnoDBのデータ保管方法を思い出すと、いつだって主キーのインデックスの先(リーフ)にレコードデータが入ってる。
そう言う保存がされてる。だから、インデックスロックと言われる。
ギャップロック
ファントムリードの防止のために存在する
ネクストキーロック
行レベルロックは、トランザクションを開始しないと即時解除される。
InnoDBの行ロックでは、基本的に更新対象ではなくスキャンした対象にロックをかける
MySQL :: MySQL 5.6 リファレンスマニュアル :: 14.2.7 ネクストキーロックによるファントム問題の回避
InnoDB は、テーブルインデックスを検索またはスキャンするときに、生成されたインデックスレコード上に共有ロックまたは排他ロックを設定するという方法で、行レベルロックを実行します。したがって、行レベルロックは、実際にはインデックスレコードロックです。
InnoDBにおけるロックの種類 | Sojiro's Blog
ギャップロックは常に共有ロックと同じ挙動 を示す
MySQL :: MySQL 5.6 リファレンスマニュアル :: 14.2.6 InnoDB のレコード、ギャップ、およびネクストキーロック
InnoDB のギャップロックは、「単に抑制的」です。つまり、ほかのトランザクションによるギャップへの挿入が停止されるだけです。したがって、ギャップ X ロックの効果はギャップ S ロックと同じです。